package Hxw3DEngine.core.materials.shader {

	import flash.display3D.Context3D;
	import flash.display3D.Context3DProgramType;
	import flash.display3D.Context3DVertexBufferFormat;
	import flash.display3D.Program3D;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.geom.Matrix3D;
	import flash.utils.ByteArray;
	
	import Hxw3DEngine.core.Resource.geometry.ResourceGeometry;
	
	import Hxw3DEngine.core.materials.shader.filters.Filter3D;
	import Hxw3DEngine.core.materials.shader.utils.FcRegisterLabel;
	import Hxw3DEngine.core.materials.shader.utils.FilterQuickSortUtils;
	import Hxw3DEngine.core.materials.shader.utils.FsRegisterLabel;
	import Hxw3DEngine.core.materials.shader.utils.ShaderRegisterCache;
	import Hxw3DEngine.core.materials.shader.utils.ShaderRegisterElement;
	import Hxw3DEngine.core.materials.shader.utils.VcRegisterLabel;
	import Hxw3DEngine.utils.AGALMiniAssembler;
	import Hxw3DEngine.proxy.Stage3DProxy;
	import Hxw3DEngine.proxy.Program3DProxy;
	  
	
	/**
	 * shader 
	 * @author Neil
	 */	
	public class Shader3D extends ShaderProgram {
				
		/** 名称 */
		public var name 			: String;
		
		private var regCache 		: ShaderRegisterCache;					// 	寄存器管理器
		private var _filters 		: Vector.<Filter3D>;					// 	效果列表
		private var _program 		: Program3D;							//	GPU指令
		private var _sourceFactor	: String;								// 	混合模式
		private var _destFactor		: String;								// 	混合模式
		private var _depthWrite 	: Boolean;								// 	开启深度
		private var _depthCompare 	: String;								// 	测试条件
		private var _cullFace 		: String;								// 	裁剪
		private var _updateShader	:Boolean = true;						//	是否要更新shader
		
		public function Shader3D(filters : Array) {
			this.name 			= "Shader3D";
			this._filters  	 	= Vector.<Filter3D>(filters);
		}
		
		/**
		 * 获取所有的filter 
		 * @return 
		 * 
		 */		
		public function get filters() : Vector.<Filter3D> {
			return this._filters;
		}
		
		/**
		 * 通过名称获取Filter 
		 * @param name
		 * @return 
		 * 
		 */		
		public function getFilterByName(name : String) : Filter3D {
			for each (var filter : Filter3D in filters) {
				if (filter.name == name) {
					return filter;
				}
			}
			return null;
		}
		
		/**
		 * 通过类型获取Filter 
		 * @param clazz
		 * @return 
		 * 
		 */		
		public function getFilterByClass(clazz : Class) : Filter3D {
			for each (var filter : Filter3D in filters) {
				if (filter is clazz) {
					return filter;
				}
			}
			return null;
		}
		
		/**
		 * 添加一个Filter 
		 * @param filter
		 * 
		 */		
		public function addFilter(filter : Filter3D) : void {
			if (filters.indexOf(filter) == -1) {
				this.filters.push(filter);
				this._updateShader  = true;
			}
		}
		
		/**
		 * 移除一个Filter 
		 * @param filter
		 * 
		 */		
		public function removeFilter(filter : Filter3D) : void {
			var idx : int = this.filters.indexOf(filter);
			if (idx != -1) {
				this.filters.splice(idx, 1);
				this._updateShader  = true;
			}
		}
		
		
		public function draw(renderable:ResourceGeometry,context3d:Context3D) : void {
			if (this._updateShader) {
				build();
			}
			for each (var filter : Filter3D in filters) {
				filter.update();
			}

			Stage3DProxy.drawCalls ++;
			
			try{
				// 设置program
				context3d.setProgram(_program);
				setContextDatas(context3d, renderable);
				context3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, regCache.vcMvp.index, Stage3DProxy.mvp, true);
				context3d.drawTriangles(renderable.IndexBuffer);
				// clear
				clearContextDatas(context3d);
			}catch (e:Error) {
				trace(e.message);	
			}
		}
		
		private function clearContextDatas(context : Context3D) : void {
			for each (var va : ShaderRegisterElement in regCache.vas) {
				if (va) {
					context.setVertexBufferAt(va.index, null);
				}
			}
			for each (var fs : FsRegisterLabel in regCache.fsUsed) {
				context.setTextureAt(fs.fs.index, null);
			}
		}
		
		/**
		 * 设置context数据 
		 * @param context	context
		 * @param surface	网格数据
		 * 
		 */		
		private function setContextDatas(context : Context3D, renderable:ResourceGeometry) : void {
			
			var i   : int = 0;
			var len : int = regCache.vas.length;
			// 设置va数据
			renderable.useVertexBuffer(context,regCache.ComponentWanted);

			// todo:RegisterLabel应该尽可能合并成一个Vector和一个ByteArray,减少常数上载的次数。
			// 设置vc数据
			for each (var vcLabel : VcRegisterLabel in regCache.vcUsed) {
				if (vcLabel.vector) {
					context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, vcLabel.vc.index, vcLabel.vector, vcLabel.num);
				} else if (vcLabel.matrix) {
					context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, vcLabel.vc.index, vcLabel.matrix, true);
				} else {
					context.setProgramConstantsFromByteArray(Context3DProgramType.VERTEX, vcLabel.vc.index, vcLabel.num, vcLabel.bytes, 0);
				}
			}
			// 设置fc
			for each (var fcLabel : FcRegisterLabel in regCache.fcUsed) {
				if (fcLabel.vector) {
					// vector频率使用得最高
					context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, fcLabel.fc.index, fcLabel.vector, fcLabel.num);
				} else if (fcLabel.matrix) {
					// matrix其次
					context.setProgramConstantsFromMatrix(Context3DProgramType.FRAGMENT, fcLabel.fc.index, fcLabel.matrix, true);
				} else {
					// bytes最后
					context.setProgramConstantsFromByteArray(Context3DProgramType.FRAGMENT, fcLabel.fc.index, fcLabel.num, fcLabel.bytes, 0);
				}
			}
			// 设置fs
			for each (var fsLabel : FsRegisterLabel in regCache.fsUsed) {
				context.setTextureAt(fsLabel.fs.index, fsLabel.texture.texture);
			}
		}
		
		/**
		 * build 
		 */		
		public function build() : void {
			this.regCache = new ShaderRegisterCache();		// 初始化寄存器管理器
			// filter排序
			FilterQuickSortUtils.sortByPriorityAsc(filters, 0 , filters.length - 1);
	
			
			// build 渲染代码
			var fragCode : String = getFragmentCode(regCache);		// build 片段着色器
			var fragDes	 : String = des;
			var vertCode : String = getVertexCode(regCache);		// build 顶点着色器
			var vertexDes: String = des;
			
			// build 注释
			
			
			// trace agal
			if (Stage3DProxy.debug) {
				trace('---------程序开始------------');
				trace('---------顶点程序------------');
				trace(vertexDes);
				trace('---------片段程序------------');
				trace(fragDes);
				trace('---------程序结束------------');
			}
			// program
			this._program = Program3DProxy.instance.creatProgram(vertCode, fragCode);
			this._updateShader = false;
		}
		
		/**
		 * 构建片段着色程序 
		 * 最先构建片段着色程序，因为只有最先构建了片段着色程序之后，在顶点程序中才只能，片段着色程序需要使用到哪些V变量。
		 */		
		override public function getFragmentCode(reg : ShaderRegisterCache) : String {
			super.getFragmentCode(reg);
			// 对oc进行初始化
			mov(regCache.oc, regCache.fc0123.yyyy);
			//拼接filter
			for each (var filter : Filter3D in filters) {
				code += filter.getFragmentCode(regCache);
				des += filter.des;
			}
			//是否有法线
			if (regCache.useNormal()) {
				code = "mov " + regCache.normalFt + ", " + regCache.getV(ResourceGeometry.NORMAL) + " \n" + code;
			}
			//最终oc
			mov("oc",regCache.oc);
			return code;
		}
		
		/**
		 * 构建顶点着色程序 
		 */		
		override public function getVertexCode(reg : ShaderRegisterCache) : String {
			super.getVertexCode(reg);
			
			// 对op进行初始化												
			mov(regCache.op, regCache.getVa(ResourceGeometry.POSITION));
			
			// 开始对v变量进行赋值,vs是所有在片段程序中使用到的v变量,通过getV()获取,vs数组索引就是surface3d对应数据类型
			var length : int = regCache.vs.length;		
			for (var i:int = 0; i < length; i++) {
				if (regCache.vs[i]) {
					mov(regCache.getV(i),regCache.getVa(i));
				}
			}
			
			// 转换法线		
			if (regCache.vs[ResourceGeometry.NORMAL]) {
				var vt0 : ShaderRegisterElement = regCache.getVt("转换法线用的VT");
				m33(vt0.xyz, regCache.getVa(ResourceGeometry.NORMAL), regCache.vcWorld);
				nrm(regCache.getV(ResourceGeometry.NORMAL).xyz,vt0.xyz);
				regCache.removeVt(vt0);
			}
			
			// 拼接filter的顶点shader
			for each (var filter : Filter3D in filters) {
				code += filter.getVertexCode(regCache);
				des += filter.des;
			}
			
			// 对filter拼接完成之后，将regCache.op输出到真正的op寄存器
			m44("op",regCache.op,regCache.vcMvp);
			return code;
		}
		
	}
}
